#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>

//触摸屏：单点触摸和多点触摸
// 单点触摸好上报  那多点触摸怎么上报呢？
// 多点触摸 5个触摸设备 5个手指同时滑动该怎么做呢？
// type B协议
// 比如5点触摸 给定5个slot编号 ABS_MT_SLOT事件的value数据便是一个slot
// ABS_MT_TRACTKING_ID 事件 携带value 表示一个ID
// slot 是硬件概念 id是软件的概念

//! 两个slot0 并不是在同一个点上奥
// 假如有一个多点触摸屏 被按下了上报顺序大概是这样的

// 1：表示创建了一个新的触摸点ID--手指按下
// 2，3：x和y的值
// 4:上报按键类事件 表示触摸点slotx
// 5 6： 又上报了一次EV_ABS x,y (只有slot0 会多上报一次 假如4是slot1是不会上报的)
// 7:同步事件表明上报完成
// 一松手的时候就会上报ABS_MT_TRACKING_ID 并把ID设置为-1

// 获取触摸屏信息

/* 用于描述MT 多点触摸的每一个触摸点的信息 */
struct ts_mt {
    int x;          //  x
    int y;          //  y
    int id;         // 对应的是ID
    int valid;      // 数据有效标志位
};

/* 一个触摸点的x坐标和y坐标*/

struct tp_xy {
    int x;
    int y;
};
static int ts_read(const int fd,const int max_slots,struct ts_mt *mt)
{
    struct input_event in_ev;
    static int slot = 0;    // 用于保存上一个slot
    static struct tp_xy xy[12] = {0}; //保存上一次的x y的坐标值 假如支持最大触点数不会超过12
    int i;
    memset(mt,0x0,max_slots * sizeof(struct ts_mt));
    for(int i = 0; i < max_slots;i++)
    {
        mt[i].id = -2;
    }
    for(;;)
    {
        if (sizeof(struct input_event) !=
            read(fd, &in_ev, sizeof(struct input_event))) {
            perror("read error");
            return -1;
        }
        switch(in_ev.type){
        case EV_ABS:    //绝对坐标
            switch(in_ev.code) {
                case ABS_MT_SLOT:
                    slot = in_ev.value;
                    break;
                // 肯定会先给slot执行 的把
                case ABS_MT_POSITION_X:
                    xy[slot].x = in_ev.value;
                    mt[slot].valid = 1;
                    break;
                case ABS_MT_POSITION_Y:
                    xy[slot].y = in_ev.value;
                    mt[slot].valid = 1;
                    break;
                case ABS_MT_TRACKING_ID:
                    mt[slot].id = in_ev.value;
                    mt[slot].valid = 1;
                    break;
            }
            break;
        // 保存了上一次的x,y的坐标进去
        case EV_SYN:
            if(SYN_REPORT == in_ev.code) {
                for(int i = 0; i < max_slots;i++) {
                    mt[i].x = xy[i].x;
                    mt[i].y = xy[i].y;
                }
            }
            return 0; 

        }
    }
}

int main(int argc ,char *argv[])
{
    struct input_absinfo slot;
    struct ts_mt *mt = NULL;
    int max_slots;
    int fd;
    int i;
    /* 参数校验 */
    if (2 != argc) {
        fprintf(stderr,"usage: %s <input_dev>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    /* 打开文件 */
    fd = open(argv[1], O_RDONLY);
    if (0 > fd) {
        perror("open error");
        exit(EXIT_FAILURE);
    }
     /* 获取触摸屏支持的最大触摸点数 */
    if (0 > ioctl(fd, EVIOCGABS(ABS_MT_SLOT), &slot)) {
        perror("ioctl error");
        close(fd);
        exit(EXIT_FAILURE);
    }
    max_slots = slot.maximum + 1 - slot.minimum;
    printf("max_slots: %d\n", max_slots);
     /* 申请内存空间并清零 */
    mt = calloc(max_slots, sizeof(struct ts_mt));
    for ( ; ; ) {
        if (0 > ts_read(fd, max_slots, mt))
            break;
        for (i = 0; i < max_slots; i++) {
            if (mt[i].valid) {//判断每一个触摸点信息是否发生更新（关注的信息发生更新）
                if (0 <= mt[i].id)
                    printf("slot<%d>, 按下(%d, %d)\n", i, mt[i].x, mt[i].y);
                else if (-1 == mt[i].id)
                    printf("slot<%d>, 松开\n", i);
                else
                    printf("slot<%d>, 移动(%d, %d)\n", i, mt[i].x, mt[i].y);
            }
        }
    }
    /* 关闭设备、退出 */
    close(fd);
    free(mt);
    exit(EXIT_FAILURE);
}